# !/usr/bin/python2.7
# -*- coding: UTF-8 -*-

###/usr/lib/python2.7/curses

import os
import sys
import shutil
import time
import datetime
import binascii
import struct
import mblue
import curses
import thread
import locale

#支持中文显示
locale.setlocale(locale.LC_ALL, "")
code = locale.getpreferredencoding()
#初始化　curses
stdscr = curses.initscr()
#获取终端可显示大小，(几行，几列)
(screen_rows, screen_columns) = stdscr.getmaxyx()
if screen_rows < 40 or screen_columns < 100:
    print "The interface is too small to display! Least 1366*768 or rows*columns > 40*100."
    curses.endwin()
    exit()
#(screen_rows, screen_columns) = os.popen('stty size', 'r').read().split()
#设置窗口大小
stdscr = curses.newwin(screen_rows,screen_columns,0,0)
#设定各显示坐标
#标题栏高度
title_h = 3
title_x = 0
title_y = 0
#菜单栏高度
menu_h = 10 
#主显示起始坐标
disp_main_x = 0
disp_main_y = title_h 
#副显示坐标
if screen_columns > 200:
    disp_fit_x = 100
    disp_fit_y = disp_main_y 
else:
    disp_fit_x = 0
    disp_fit_y = disp_main_y 
#菜单显示坐标
disp_menu_x = 0 
disp_menu_y = screen_rows - menu_h
#主副可显示行数
main_fit_max_line = disp_menu_y - title_h 

inputstr = ''

def display_info(str, x, y, colorpair=2):
    '''''使用指定的colorpair显示文字'''  
    global stdscr
    stdscr.addstr(y, x,str, curses.color_pair(colorpair))
    stdscr.refresh()

def get_ch_and_continue():
    '''''演示press any key to continue'''
    global stdscr
    #设置nodelay，为0时会变成阻塞式等待
    stdscr.nodelay(0)
    #输入一个字符
    ch=stdscr.getch()
    #重置nodelay,使得控制台可以以非阻塞的方式接受控制台输入，超时1秒
    stdscr.nodelay(1)
    return True

def get_str_keyboard(x,y): 
    global stdscr
    global inputstr 
    stdscr.nodelay(1)
    ch = stdscr.getch()
    if ch == curses.KEY_UP:#↑
        return '↑'
    if ch == curses.KEY_DOWN:#↓
        return '↓'
    if ch == curses.KEY_RIGHT:#→
        return '→'
    if ch == curses.KEY_LEFT:#'←'
        return '←'
    if ch == curses.KEY_F5:#'←'
        return 'F5'
    if ch == curses.KEY_BACKSPACE:#'Backspace'
        inputstr = inputstr[:-1]
        display_info('%32s' % (''),x,y,1)
        display_info(inputstr,x,y,1)
        return False
    if ch == 27:
        return 'Esc'
    if ch == 24:
        return 'Ctrl+x'
    if ch == 5:
        return 'Ctrl+e'
    if ch == 2:
        return 'Ctrl+b'
    if ch == 0x0A or ch == 0x0D:
        #display_info('%32s' % (''),x,y,1)
        if not inputstr:
            return 'Enter'
        instr = inputstr
        #inputstr = ''
        return instr
    elif ch < 128 and ch >= 0:
        inputstr = inputstr + chr(ch & 0x7f)
        display_info(inputstr,x,y,1)
    return False 

def set_win():
    '''''控制台设置'''
    global stdscr
    #使用颜色首先需要调用这个方法
    curses.start_color()
    #文字和背景色设置，设置了四个color pair，分别为1和2
    curses.init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK)
    curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK)
    curses.init_pair(3, curses.COLOR_BLACK, curses.COLOR_GREEN)
    curses.init_pair(4, curses.COLOR_RED, curses.COLOR_WHITE)
    #隐藏光标
    curses.curs_set(0)
    #关闭屏幕回显
    curses.noecho()
    #输入时不需要回车确认
    curses.cbreak()
    #使用键盘上一些特殊字符,会将方向、Fx等转换为特殊序列
    stdscr.keypad(1)
    #设置nodelay，使得控制台可以以非阻塞的方式接受控制台输入，超时1秒
    stdscr.nodelay(1)

def unset_win():
    '''控制台重置'''
    global stdstr
    #恢复控制台默认设置（若不恢复，会导致即使程序结束退出了，控制台仍然是没有回显的）
    curses.nocbreak()
    stdscr.keypad(0)
    curses.echo()
    #结束窗口
    curses.endwin()

pkgtype = [ 'None',\
            'REALTIMEDATA(01)',\
            'DEVICEIDENT(02)',\
            'DEVICEIDENTSET(03)',\
            'STATISTICSDATA(04)',\
            'None','None',\
            'DEVICEQUERY(07)',\
            'SYSTEMSETUP(08)',\
            'ACTIONRECORD(09)',\
            'SPECIALTEST(0A)',\
            'SPECIALTESTSCORETURN(0B)',\
            'COMMUNICATIONRECEIPT(0C)',\
            'None','None','None','None',\
            'REALTIMEDATARETRANSRETURN(11)',\
            'None','None',\
            'STATISTICSDATARETRANSRETURN(14)',\
            'None','None','None','None',\
            'ACTIONRECORDRETRANSRETURN(19)',\
            'None','None','None','None','None','None','None',\
            'SYNCRTDATACMD(21)',\
            'None','None','None','None','None','None','None',\
            'MOTIONRECORDRETRANS(29)',\
            'GPSUPPLEMENT(A0)',\
            'None']

def displayMenu(x,y):
    display_info('%-s' % (((disp_fit_x==0) and (89) or (screen_columns)) * ">"),x,y,3)
    display_info('%-100s' % '',x,y+1)
    display_info(' You can choose the following:',x,y+2,1)
    display_info(' (1) Press \'Esc\' to return or input \'quit\' to exit',x,y+3,1)
    display_info(' (2) Press \'F5\' Re scan',x,y+4,1)
    display_info(' (3) Press \'↑ \' or \'↓ \' to select and then press \'Enter\' to view details',x,y+5,1)
    display_info(' (4) Input \'connect (S/N)\' to connect which you select',x,y+6,1)
    display_info(' Input:   ',x,y+7,1)
    return x,y+7

def get_resp_input(yy,devdata,displaymode,lineindex):
    instr = get_str_keyboard(10,yy)
    if not instr is False:
        #display_info('%16s' % instr,0,i+6)
        if instr == 'quit' or instr == 'Ctrl+e':
            return False
        if instr == 'Esc' or instr == 'Ctrl+b':
            if displaymode == 'Scan':
               return False 
            elif displaymode == 'details' or displaymode == 'connect':
                displaymode = 'Scan'
            stdscr.clear()
        elif instr == '↓':
            lineindex = lineindex + 1
            if lineindex >= len(devdata['mac']):
                lineindex = 0
        elif instr == '↑':
            lineindex = lineindex - 1
            if lineindex < 0:
                lineindex = len(devdata['mac']) - 1
        elif instr == 'F5':
            if displaymode == 'Scan':
                devdata = {'mac':[],'name':[],'rssi':[],'pkgtype':[],'data':[]}
                stdscr.clear()
                display_info('Scan devices ...',title_x,title_y)
        elif instr == 'Enter':
            if displaymode == 'Scan' and lineindex != -1:
                displaymode = 'details'
                stdscr.clear()
            display_info(' Enter %d' % lineindex,disp_menu_x,disp_menu_y + 1)
        elif 'connect' in instr.split(' ') or instr == 'Ctrl+x':
            ss = instr.split(' ')
            if len(ss) > 1 and ss[1].isdigit():
                lineindex = int(ss[1])
                displaymode = 'connect'
            elif lineindex == -1:
                display_info('%-32s' % ('input error'),10,yy,1)
            else:
                displaymode = 'connect'
            stdscr.clear()
    return devdata,displaymode,lineindex 

def fill_scan_info(dev,devdata):
    devs = dev.scan(mblue.ScanDelegate(True,False,whitelist="",save2file=False,disp=False),timeout=0.3)
    for device in devs:
        for (adtype, desc, value) in device.getScanData():
            if desc is 'Manufacturer' and len(value) > 6 and value[0:4] == 'ff02':
                if device.addr not in devdata['mac']:
                    devdata['mac'].append(device.addr)
                    devdata['name'].append('None')
                    devdata['rssi'].append(device.rssi)
                    devdata['pkgtype'].append(pkgtype[(int(value[4:6],16) >= len(pkgtype) and len(pkgtype)-1 or int(value[4:6],16))][-4:])
                    devdata['data'].append(value[4:])
                else:
                    index = devdata['mac'].index(device.addr)
                    devdata['rssi'][index] = int(device.rssi)
                    devdata['pkgtype'][index] = pkgtype[(int(value[4:6],16) >= len(pkgtype) and len(pkgtype)-1 or int(value[4:6],16))][-4:]
                    devdata['data'][index] = value[4:] 
            if desc == ('Short Local Name' or 'Complete Local Name'):
                if device.addr not in devdata['mac']:
                    devdata['mac'].append(device.addr)
                    devdata['name'].append(value[0:(len(value) > 8 and 8 or len(value))])
                    devdata['rssi'].append(device.rssi)
                    devdata['pkgtype'].append(device.addrType)
                    devdata['data'].append('')
                else:
                    index = devdata['mac'].index(device.addr)
                    devdata['name'][index] = value[0:(len(value) > 8 and 8 or len(value))]
    return devdata

def disp_scan(devdata,lineindex):
    display_info('Scan devices ...',title_x,title_y)
    display_info(' %-3s %-17s  %-8s %-4s(dB) %-8s %-38s' % ('S/N','MAC','NAME','RSSI','PKGTYPE','DATA'),title_x,title_y+2,3)
    y = 0
    for y in range(0,len(devdata['mac'])):
        if lineindex >= 0 and y == lineindex:
            white = 4
        else:
            white = 2
        if y > (((disp_fit_x==0) and (main_fit_max_line) or (main_fit_max_line*2))-1):
            break;
        if y > (main_fit_max_line-1):
            if y == main_fit_max_line+1:
                display_info(' %-3s %-17s  %-8s %-4s(dB) %-8s %-38s' % ('S/N','MAC','NAME','RSSI','PKGTYPE','DATA'),disp_fit_x,title_y+2,3)
            display_info(' %-3d %-17s  %-8s  %-7s  %-7s %s' % (y,devdata['mac'][y],devdata['name'][y],devdata['rssi'][y],devdata['pkgtype'][y],devdata['data'][y]),disp_fit_x,y+disp_fit_y-(main_fit_max_line),white)
        else:
            display_info(' %-3d %-17s  %-8s  %-7s  %-7s %s' % (y,devdata['mac'][y],devdata['name'][y],devdata['rssi'][y],devdata['pkgtype'][y],devdata['data'][y]),disp_main_x,disp_main_y+y,white)
    return (y+disp_main_y) 

parsadvd=['None' for i in range(50)]
def pars_time(t):
    tt = bin(int(t[6:8] + t[4:6] + t[2:4] + t[0:2],16))[2:]
    year = int(tt[0:-26],2)+2010
    mon = int(tt[-26:-22],2)+1
    day = int(tt[-22:-17],2)
    hour = int(tt[-17:-12],2)
    minu = int(tt[-12:-6],2)
    sec = int(tt[-6:],2)
    return str(year)+'/'+str(mon)+'/'+str(day)+' '+str(hour)+':'+str(minu)+':'+str(sec)

def pars_realtimepkg(data):
    realtime = pars_time(data[0:8]) 
    longitude = str(int(data[8:10],16)+int(data[10:12],16)*256) 
    latitude = str(int(data[12:14],16)+int(data[14:16],16)*256)
    hrm = str(int(data[16:18],16))+' bpm'
    shortspeed = str(float(int(data[18:20],16))/10)+' m/s'
    accdis = str(int(data[20:22],16)+int(data[22:24],16)*256)+' m'
    accload = str(int(data[24:26],16)+int(data[26:28],16)*256)
    accexload = str(int(data[28:30],16)+int(data[30:32],16)*256)
    return realtime,longitude,latitude,hrm,shortspeed,accdis,accload,accexload

def pars_iden_pkg(data):
    usrsn = str(data[6:8] + data[4:6] + data[2:4] + data[0:2])
    netip = str(data[14:16]+':'+data[12:14]+':'+data[10:12]+':'+data[8:10])
    netiip = str(int(data[16:18],16))
    devsta = str(bin(int(data[18:20],16))[2:].zfill(8))
    selfsta = str(bin(int(data[20:22],16))[2:].zfill(8))
    #devname = str(data[32:34]+data[30:32]+data[28:30]+data[26:28]+data[24:26]+data[22:24]) 
    devname = data[22:34] 
    devname = ''.join(map(chr,[int(devname[i:i+2],16) for i in xrange(0, len(devname), 2)]))
    return usrsn,netip,netiip,devsta,selfsta,devname 

ACTION_TYPE=['','冲刺','冲撞','跳跃','转向',' ']
def pars_action_pkg(data):
    starttime = pars_time(data[0:8])
    holdtime = str(int(data[8:10],16)+int(data[10:12],16)*256)+'ms'
    actiontype = ACTION_TYPE[(int(data[12:14],16) > 4 and 5 or int(data[12:14],16))]
    acccount = str(int(data[14:16],16)+int(data[16:18],16)*256)
    return starttime,holdtime,actiontype,acccount

def pars_A1_pkg(data):
    hravg = str(int(data[2:4],16))+' bpm'
    hrmx = str(int(data[4:6],16))+' bpm'
    speedavg = str(int(data[8:10],16))+' m/s'
    speedmx = str(int(data[10:12],16))+' m/s'
    inload = str(int(data[14:16],16))
    exload = str(int(data[16:18],16))
    enlevel = str(int(data[20:22],16))+' %'
    return hravg,hrmx,speedavg,speedmx,inload,exload,enlevel

def pars_advdata(data):
    global parsadvd
    if data[0:2] == '01':#实时包
        parsadvd[0:8] = pars_realtimepkg(data[4:])
    elif data[0:2] == '02':#识别包
        parsadvd[8:14] = pars_iden_pkg(data[4:])
    elif data[0:2] == '09':#动作包
        parsadvd[14:18] = pars_action_pkg(data[4:])
    elif data[0:2] == '04':#统计包
        parsadvd[18] = pars_time(data[4:12])
        if data[14:16] == '13':#统计A1包
            parsadvd[19:26] = pars_A1_pkg(data[14:])

    return parsadvd

def scan_details(devdata,index):
    if index >= len(devdata['mac']):
        return False
    if len(devdata['data'][index]) != 38:
        return False
    display_info('Scan details %s ' % devdata['mac'][index],title_x,title_y)
    display_info('%-s' % (((disp_fit_x==0) and (89) or (screen_columns)) * "<"),title_x,title_y+2,3)
    advd = pars_advdata(devdata['data'][index])
    #实时包
    display_info('包类型: %-20s' % ('实时包'),disp_main_x,disp_main_y+1)
    display_info('时　间: %-20s' % (advd[0]),disp_main_x,disp_main_y+2)
    display_info('经　度: %-20s' % (advd[1]),disp_main_x,disp_main_y+3)
    display_info('纬　度: %-20s' % (advd[2]),disp_main_x,disp_main_y+4)
    display_info('心　率: %-20s' % (advd[3]),disp_main_x,disp_main_y+5)
    display_info('短速度: %-20s' % (advd[4]),disp_main_x,disp_main_y+6)
    display_info('累　距: %-20s' % (advd[5]),disp_main_x,disp_main_y+7)
    display_info('累　荷: %-20s' % (advd[6]),disp_main_x,disp_main_y+8)
    display_info('累外荷: %-20s' % (advd[7]),disp_main_x,disp_main_y+9)
    #识别包
    display_info('包类型: %-20s' % ('识别包'),disp_main_x,disp_main_y+11)
    display_info('序列号: %-20s' % (advd[8]),disp_main_x,disp_main_y+12)
    display_info('网编号: %-20s' % (advd[9]),disp_main_x,disp_main_y+13)
    display_info('网内号: %-20s' % (advd[10]),disp_main_x,disp_main_y+14)
    display_info('设状态: %-20s' % (advd[11]),disp_main_x,disp_main_y+15)
    display_info('自状态: %-20s' % (advd[12]),disp_main_x,disp_main_y+16)
    display_info('设备名: %-20s' % (advd[13]),disp_main_x,disp_main_y+17)
    #动作包
    display_info('包类型: %-20s' % ('动作包'),disp_main_x,disp_main_y+19)
    display_info('始时间: %-20s' % (advd[14]),disp_main_x,disp_main_y+20)
    display_info('持续时: %-20s' % (advd[15]),disp_main_x,disp_main_y+21)
    display_info('类　型: %-20s' % (advd[16]),disp_main_x,disp_main_y+22)
    display_info('累次数: %-20s' % (advd[17]),disp_main_x,disp_main_y+23)
    #统计包A1
    display_info('统计A1: %-20s' % ('统计A1包'),disp_main_x+30,disp_main_y+1)
    display_info('始时间: %-20s' % (advd[18]),disp_main_x+30,disp_main_y+2)
    display_info('均心率: %-20s' % (advd[19]),disp_main_x+30,disp_main_y+3)
    display_info('心率MX: %-20s' % (advd[20]),disp_main_x+30,disp_main_y+4)
    display_info('均速度: %-20s' % (advd[21]),disp_main_x+30,disp_main_y+5)
    display_info('速度MX: %-20s' % (advd[22]),disp_main_x+30,disp_main_y+6)
    display_info('内负荷: %-20s' % (advd[23]),disp_main_x+30,disp_main_y+7)
    display_info('外负荷: %-20s' % (advd[24]),disp_main_x+30,disp_main_y+8)
    display_info('电　量: %-20s' % (advd[25]),disp_main_x+30,disp_main_y+9)
    
    return

def connect_dev(devdata,index):
    if index >= len(devdata['mac']):
        return False
    #display_info('Connect to %s ' % devdata['mac'][index],title_x,title_y)
    #cmd = 'sudo python blue_conn.py mac 00:0B:57:1D:38:10 uuid 1101 chid 8888 feedp 170611062205 1000'
    #os.system(cmd)
    os.system('python blueconn.py mac %s' % (devdata['mac'][index]))
    return True


if __name__ == '__main__':

    devdata = {'mac':[],'name':[],'rssi':[],'pkgtype':[],'data':[]}
    displaymode = 'Scan'
    try:
        dev = mblue.bluedev()
        set_win()
        lineindex = -1
        while True:
            devdata = fill_scan_info(dev,devdata)
            if displaymode == 'Scan':
                disp_scan(devdata,lineindex)
            elif displaymode == 'details':
                scan_details(devdata,lineindex)
            elif displaymode == 'connect':
                connect_dev(devdata,lineindex)
                set_win()
                displaymode = 'Scan'

            #display_info('%-16d'%(stdscr.getch()),disp_menu_x,disp_menu_y + 1)

            yy = displayMenu(disp_menu_x,disp_menu_y)[1]
            rsp = get_resp_input(yy,devdata,displaymode,lineindex)
            if rsp == False:
                break;
            if len(rsp) == 3:
                (devdata,displaymode,lineindex) = rsp

    except Exception,e:
        raise e
    finally:
        #display_info('Press any key to continue...',0,(len(devdata['mac']) + 5))
        #get_ch_and_continue()
        unset_win()  
    exit()

    
